# 基准测试

+ 使用[sofaload](https://github.com/antJack/sofaload)在本地搭建简单的性能测试
```Bash
./sofaload -D 10 --qps=2000 -c 200 -t 16 -p sofarpc sofarpc://127.0.0.1:12200
```

```Bash
starting benchmark...
Application protocol: sofarpc

finished in 10.01s, 2000.00 req/s, 2.41MB/s
requests: 20000 total, 20000 started, 20000 done, 20000 succeeded, 0 failed, 0 errored, 0 timeout
sofaRPC status codes: 
	20000 success, 0 error, 0 server exception, 0 unknown
	0 server threadpool busy, 0 error comm, 0 no processor, 0 timeout
	0 client send error, 0 codec exception, 0 connection closed, 0 server serial exception
	0 server deserial exception
traffic: 24.11MB (25280000) total, 390.63KB (400000) headers (space savings 0.00%), 23.73MB (24880000) data
                     min         max         mean         sd        +/- sd
time for request:      130us      5.09ms       248us       189us    94.62%
time for connect:        4us       139us        29us        37us    88.00%
req/s           :       9.50       10.60       10.00        0.41    74.50%

  Latency  Distribution
   50%        204us
   75%        268us
   90%        365us
   95%        446us
   99%        680us
```

# 功能测试

## grpc server 的支持

+ 接入MOSN集成测试

```Bash
--- ServerStreamingEcho ---
timestamp from metadata:
 0. Jun  4 15:20:22.909831000
request received: message:"this is server streaming metadata"
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
echo message this is server streaming metadata
    framework.go:32: finish case: server streaming
    framework.go:30: run case: client streaming
--- ClientStreamingEcho ---
timestamp from metadata:
 0. Jun  4 15:20:22.912552000
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
request received: message:"this is client streaming metadata" , building echo
echo last received message
    framework.go:32: finish case: client streaming
    framework.go:30: run case: bidrectional
--- BidirectionalStreamingEcho ---
timestamp from metadata:
 0. Jun  4 15:20:22.914416000
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
request received message:"this is bidrectional metadata" , sending echo
    framework.go:32: finish case: bidrectional
```

## tls 证书内存优化

### 模拟服务端代码

```Go
func StartServer(mtls bool, c int, p string) {
        go http.ListenAndServe("0.0.0.0:23456", nil)

        ln, err := net.Listen("tcp", "127.0.0.1:2045")
        if err != nil {
                panic(err)
        }
        // CreateTLSConfig will read and parse a total of c certificates from path p, and makes a tls.Config
        // if mtls is true, the ClientAuth will be setted to  RequireAndVerifyClientCert
       // see details in attachment: test.tar.gz
        tlsLn := tls.NewListener(ln, CreateTLSConfig(mtls, c, p))
        for {
                conn, err := tlsLn.Accept()
                if err != nil {
                        fmt.Println("accept conn error: ", conn)
                        return
                }
                go func() {
                        for {
                                b := make([]byte, 10)
                                conn.SetDeadline(time.Time{})
                                n, err := conn.Read(b)
                                if err != nil {
                                        fmt.Println("connection read error:", err)
                                        return
                                }
                                if n > 0 {
                                        _, _ = conn.Write(b)
                                }
                        }
                }()
        }
}
``` 
### 模拟客户端代码

```Go
func StartClient(mtls bool) {
        go http.ListenAndServe("0.0.0.0:12345", nil)

        pool := x509.NewCertPool()
        pool.AppendCertsFromPEM([]byte(midCa))
        config := &tls.Config{
                RootCAs:    pool,
                ServerName: "127.0.0.1",
        }
        // add tls certificate
        if mtls {
                ca := GetIntermediateCa()
                cert := ca.CreateCertificatePemJson()
                c, err := tls.X509KeyPair([]byte(cert.Cert), []byte(cert.Key))
                if err != nil {
                        panic(err)
                }
                config.Certificates = []tls.Certificate{c}
        }

        connTotal := 20000

        for i := 0; i < connTotal; i++ {
                conn, err := tls.Dial("tcp", "127.0.0.1:2045", config)
                if err != nil {
                        fmt.Println("dial error: ", err)
                        return

                }
                defer conn.Close()
                go func() {
                        conn.SetReadDeadline(time.Time{})
                        if _, err := conn.Write([]byte("test data")); err != nil {
                                fmt.Println("write error: ", err)
                                return
                        }
                        buf := make([]byte, 10)
                        if _, err := conn.Read(buf); err != nil {
                                fmt.Println("read error: ", err)
                                return
                        }
                }()
        }
        fmt.Printf("connected %d conns\n", connTotal)
        // hang up, makes connection exists
        ch := make(chan struct{})
        <-ch

}
```

### 测试数据

| version  | scenario | client mem usage | server mem usage |
| ------------- | ------------- | ------------- | ------------- |
| original  | 20,000 client connections, tls   | 348.3m | 282.7m |
| cache  | 20,000 client connections, tls   | 205.2m | 282.1m |
| original  | 20,000 client connections, mtls   | 372.4m | 508.5m |
| cache  | 20,000 client connections, mtls   | 230.2m | 356.0m |

## so plugin 扩展协议实现

+ client mosn
```Bash
[in decodeRequest]
[out decodeRequest] payload: Hello World
[in encodeRequest] request: Hello World
[out encodeRequest]
[in decodeResponse]
[out decodeRequest] payload: Hello, I am server
[in encodeResponse] response: Hello, I am server
[out encodeResponse]
```
+ server mosn
```Bash
[in decodeRequest]
[out decodeRequest] payload: Hello World
[in encodeRequest] request: Hello World
[out encodeRequest]
[in decodeResponse]
[out decodeRequest] payload: Hello, I am server
[in encodeResponse] response: Hello, I am server
[out encodeResponse]
```
